/*
 * csv.c - CSV output
 *
 * Copyright (C) 2015 Intel Corporation
 *
 * cve-check-tool is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#define _GNU_SOURCE

#include <errno.h>
#include <stdio.h>

#include "config.h"
#include "cve-check-tool.h"
#include "plugin.h"
#include "util.h"

static inline bool filter_item(CveCheckTool *self, __attribute__((unused)) GList *item)
{
        struct cve_entry_t *c_entry = cve_db_get_cve(self->cve_db, (gchar *)item->data);
        bool ret = false;
        if (self->modified > 0 && c_entry->modified > self->modified) {
                ret = true;
        }
        cve_free(c_entry);
        return ret;
}

static gchar *list_as_string(CveCheckTool *self, GList *list)
{
        GList *it = NULL;
        gchar *ret = NULL;
        if (!list) {
                return NULL;
        }

        if (!filter_item(self, list)) {
                ret = g_strdup_printf("%s", (gchar *)list->data);
        }

        if (!list->next) {
                return ret;
        }

        for (it = list->next; it; it = it->next) {
                if (filter_item(self, it)) {
                        continue;
                }
                gchar *next = NULL;
                next = g_strdup_printf("%s %s", ret, (gchar *)it->data);
                if (ret) {
                        g_free(ret);
                }
                ret = next;
        }
        return ret;
}

static int get_open_bug_count(CveCheckTool *self, const gchar *cves)
{
        int bug_count = 0;
        gchar **ids, **id = NULL;
        ReportStatus report_status = REPORT_STATUS_UNREPORTED;

        if (cves == NULL) {
                return bug_count;
        }
        ids = g_strsplit(cves, " ", -1);
        for (id = ids; *id; id++) {
                if (g_hash_table_contains(self->bdb, *id)) {
                        report_status = *((ReportStatus *)g_hash_table_lookup(self->bdb, *id));
                        if (report_status == REPORT_STATUS_OPEN) {
                                ++bug_count;
                        }
                }
        }
        g_strfreev(ids);
        return bug_count;
}

static bool csv_write_report(CveCheckTool *self)
{
        GHashTableIter iter;
        gchar *key = NULL;
        struct source_package_t *v = NULL;

        FILE *fd = NULL;
        bool ret = false;

        if (self->output_file) {
                fd = fopen(self->output_file, "w");
                if (!fd) {
                        fprintf(stderr, "Unable to open %s for writing: %s\n", self->output_file, strerror(errno));
                        return false;
                }
        } else {
                fd = stdout;
        }

        /* package,version,unpatched CVE numbers space delimited,patched CVE numbers space delimited, open bug count */
        g_hash_table_iter_init(&iter, self->db);
        while (g_hash_table_iter_next(&iter, (void **)&key, (void **)&v)) {
                autofree(gchar) *issues = NULL;
                autofree(gchar) *patched = NULL;
                autofree(gchar) *open_bug_count = NULL;
                gchar *is = NULL, *pa = NULL;

                if (!v->issues && !v->patched && !self->show_unaffected) {
                        continue;
                }
                if (!v->issues && self->hide_patched) {
                        continue;
                }
                is = issues = list_as_string(self, v->issues);
                if (!is) {
                        is = "";
                }
                pa = patched = list_as_string(self, v->patched);
                if (!pa) {
                        pa = "";
                }
                if (!self->show_unaffected && !issues && !patched) {
                        continue;
                }
                if (self->bdb) {
                        open_bug_count =
                            g_strdup_printf("%i", get_open_bug_count(self, is) + get_open_bug_count(self, pa));
                } else {
                        open_bug_count = g_strdup("0");
                }
                if (fprintf(fd, "%s,%s,%s,%s,%s\n", key, (char *)v->version, is, pa, open_bug_count) < 0) {
                        goto io_error;
                }
        }

        ret = true;
        goto success;

io_error:
        fprintf(stderr, "Error writing to file: %s\n", strerror(errno));
success:
        ret = true;
        if (fd != stdout && self->output_file) {
                fclose(fd);
        }

        return ret;
}

_module_export_ bool cve_plugin_module_init(CvePlugin *self)
{
        self->report = csv_write_report;
        self->flags = PLUGIN_TYPE_REPORT;
        self->name = "csv";
        return true;
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 expandtab:
 * :indentSize=8:tabSize=8:noTabs=true:
 */
